import {createRouter, createWebHistory, RouteRecordRaw} from "vue-router";
import { dynamicRoutes, staticRoutes} from "@/router/modules/constant.ts";
import * as NProgress from "nprogress";


// NProgress 进度条配置。官方文档 https://github.com/rstacruz/nprogress
NProgress.configure({
    easing: 'ease',
    speed: 500,
    trickleSpeed: 200,
    showSpinner: false,
});

// 每一个 views 文件夹下目录对应一个路由模块文件，避免在一个文件中写太多代码。
/** Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块。匹配到的文件默认是懒加载的，通过动态导入实现，可以有效减少打包体积。也可以配置为 eager，即立即一次性加载。
 * 如何匹配所有文件请看： https://cn.vitejs.dev/guide/features#glob-import
 * 如何排除文件请看： https://cn.vitejs.dev/guide/features#negative-patterns
 */
// modules 是一个对象，其键为文件路径，值为模块对象。
const modules: Record<string, any> = import.meta.glob(
    ["./modules/**/*.ts", "!./modules/**/constant.ts", "!./modules/**/dynamic.ts"],
    {
        eager: true
    }
);

/*
以上动态导入会使  vite 生成类似如下的代码
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
    './dir/foo.js': __glob__0_0,
    './dir/bar.js': __glob__0_1,
}
*/

// 获取所有模块对象，并将将每个模块的 default 导出展开并合并成一个数组。
const routes: Array<RouteRecordRaw> = Object.values(modules)
    // 展开每个模块的 default 导出
    .flatMap(module => module.default)
    // 过滤出所有带有 order 属性的路由，确保每个路由都有 order 属性。只有 Layout 路由配置 order 属性。参与排序。
    .filter(route => route.meta && route.meta.order !== undefined)
    // 根据 order 属性排序, 数字越小越靠前。
    .sort((a, b) => a.meta.order - b.meta.order);
console.log("routes 路由模块：", routes)


// 创建路由
// TODO 所有的 store 中存放的数据都持久化到 localStorage 中，可以使用 pinia-plugin-persistedstate 插件完成
const router = createRouter({
    // 历史模式，默认为 hash 模式，使用 history 模式需要服务器支持。官方推荐使用
    history: createWebHistory(),
    routes: routes, // 将定义的路由传入
    // 参考 vue-router 官方文档： https://router.vuejs.org/zh/guide/essentials/route-matching-syntax.html#Sensitive-%E4%B8%8E-strict-%E8%B7%AF%E7%94%B1%E9%85%8D%E7%BD%AE
    // 匹配路由时是否启用尾部斜杠。如果启用，则 /test/abc 将匹配 /test/abc，但是 /test/abc/ 将不会匹配。
    strict: true,
    // 匹配路由时是否启用大小写不敏感。如果启用，则 /test/abc 将匹配 /test/abc，但是 /test/ABC 将不会匹配。
    sensitive: true,
    // 滚动行为，当切换到新路由时，想要页面滚到顶部，或者是保持原先的滚动位置，就像重新加载页面那样。
    // 关于滚动行为的详细信息，请参考官方文档： https://router.vuejs.org/zh/guide/advanced/scroll-behavior.html
    scrollBehavior(to, from, savedPosition) {
        // 始终滚动到顶部
        return { top: 0 }
    },
});


// TODO 每次路由导航都需要鉴权，参考官方： https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
// 使用 router.beforeEach 注册一个全局前置守卫，在导航被触发时，该守卫会被调用。可以用来验证用户是否登录，或者根据用户权限进行路由跳转。
/*
* 全局前置守卫有三个参数：
* to: RouteLocationNormalized 即将要进入的目标路由
* from: RouteLocationNormalized 当前导航即将离开的路由
* next: NavigationGuardNext 是一个函数，调用 next() 则导航可以成功，调用 next(false) 则导航会被终止，并通过一个错误回调函数 (errorCb) 被传到 router.onError()。
* 注意： 一旦调用了 next()，守卫中的后续逻辑将不会继续执行
*/
// 添加全局前置守卫
router.beforeEach(async (to, from, next) => {
    // 开始显示进度条
    NProgress.start()

    // TODO: 后续添加加载动画效果

    // 检查路由是否存在
    // 在路由导航开始之前先检查要去的路由是否存在，如果存在则继续下面的内容；如果不存在则重定向到 404 页面
    if (!router.hasRoute(to.name)) {
        // 路由不存在，重定向到 404 页面
        next("/error/404")
    } else {
        // to.meta.requiresAuth：仅检查当前目标路由的元数据
        // to.matched.some(record => record.meta.requiresAuth)：检查当前目标路由及其所有父级路由的元数据。
        // 检查是否有特定的权限要求
        if (to.meta.requiresAuth && !isUserAuthenticated()) {
            // 如果需要认证且用户未认证，重定向到登录页面.
            next({
                path: '/login/glass',
                query: { redirect: to.fullPath } // 保存重定向前的路径，以便登录成功后返回
            });
        } else if (to.meta.permission === 'admin' && !isAdmin()) {
            // 如果需要管理员权限且用户不是管理员，重定向到 403 页面
            next('/error/403');
        } else {
            // 否则，正常导航。去到目标 to 路由
            next();
        }
    }


    // const isAuthenticated = await auth.isAuthenticated()
    const isAuthenticated = true
    // 如果用户要访问的路由不是登录页面，并且前往的路由需要授权，则重定向到登录页面
    if (to.name !== 'Login' && !isAuthenticated) {
        next({ name: 'Login' })
    } else {
        // 继续执行导航
        next()
    }
})

// 添加全局后置守卫
// 全局后置守卫在导航被确认之后，在组件的 beforeRouteEnter 守卫和 asyncData 钩子被解析之后执行。
// 它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
router.afterEach((to, from) => {
    // 结束进度条
    NProgress.done()
    // 路由过渡动效，参考 vue-router官方文档： https://router.vuejs.org/zh/guide/advanced/transitions.html
    const toDepth = to.path.split('/').length
    const fromDepth = from.path.split('/').length
    to.meta.transition = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})

// 导航故障，或者叫导航失败，表示一次失败的导航，处理路由错误
// 添加一个错误处理程序，每当导航过程中发生未捕获的错误时都会调用该程序。
// 这包括同步和异步抛出的错误，在任何导航保护中返回或传递给next的错误，以及在尝试解析呈现路由所需的异步组件时发生的错误。
// 直接参数为一个复合类型 _ErrorHandler ,用于包装多个参数。
// 路由发生错误后销毁进度条
router.onError((error, to, from) => {
    // 结束进度条
    NProgress.remove()
    console.log(error)
    console.log(to.fullPath)
    console.log(from.fullPath)
    // TODO 根据错误类型重定向到对应页面
    router.push("/error/404").then(failure => {
        console.log(failure)
    })
})

// 示例函数，根据实际情况替换
function isUserAuthenticated() {
    // 检查用户是否已认证
    return localStorage.getItem('Authorization') !== null;
}

function isAdmin() {
    // 检查用户是否有管理员权限
    const user = JSON.parse(localStorage.getItem('user'));
    return user && user.role === 'admin';
}

// TODO 动态路由，对路由的添加通常是通过 routes 选项来静态地定义完成的，但是在某些情况下，你可能想在应用程序已经运行的时候添加或删除路由。
// 这时候，你可以使用 router.addRoute() 和 router.removeRoute() 方法。
// 例如，你可能想在用户登录成功后动态添加一些路由，或者在用户权限发生变化时动态更新路由。
// 这些方法不会触发导航，因此你需要自己处理路由跳转。
// 关于动态路由的使用，请参考官方文档： https://router.vuejs.org/zh/guide/advanced/dynamic-routing.html

// 将创建的router路由暴露，使其在其他地方可以被引用
export default router;